#include "VStdAfx.h"
#include "VImage.h"

#if 0
// Move To Editor 
/******************************************************************************\
*
* Rotate image
*
\******************************************************************************/

/*
 */
template <class Type>
static void rotate_cw(BYTE *dest,const BYTE *src,int width,int height,int components) {
	Type *d = (Type*)dest;
	for(int y = 0; y < height; y++) {
		for(int x = 0; x < width; x++) {
			const Type *s = (const Type*)src + ((width - x - 1) * height + y) * components;
			for(int i = 0; i < components; i++) {
				*d++ = *s++;
			}
		}
	}
}

template <class Type>
static void rotate_ccw(BYTE *dest,const BYTE *src,int width,int height,int components) {
	Type *d = (Type*)dest;
	for(int y = 0; y < height; y++) {
		for(int x = 0; x < width; x++) {
			const Type *s = (const Type*)src + ((x + 1) * height - y - 1) * components;
			for(int i = 0; i < components; i++) {
				*d++ = *s++;
			}
		}
	}
}

template <class Type>
static void rotate_copy(BYTE *dest,const BYTE *src,int width,int height,int components) {
	Type *d = (Type*)dest;
	const Type *s = (const Type*)src;
	for(int y = 0; y < height; y++) {
		for(int x = 0; x < width * components; x++) {
			*d++ = *s++;
		}
	}
}

template <class Type>
static void rotate_cube_cw(BYTE *dest,const BYTE *src,int width,int height,size_t size,int components) {
	rotate_cw<Type>(dest + size * 4,src + size * 0,width,height,components);
	rotate_ccw<Type>(dest + size * 5,src + size * 1,width,height,components);
	rotate_cw<Type>(dest + size * 1,src + size * 2,width,height,components);
	rotate_cw<Type>(dest + size * 0,dest + size * 1,width,height,components);
	rotate_copy<Type>(dest + size * 1,src + size * 3,width,height,components);
	rotate_cw<Type>(dest + size * 2,src + size * 4,width,height,components);
	rotate_cw<Type>(dest + size * 3,src + size * 5,width,height,components);
}

template <class Type>
static void rotate_cube_ccw(BYTE *dest,const BYTE *src,int width,int height,size_t size,int components) {
	rotate_cw<Type>(dest + size * 3,src + size * 0,width,height,components);
	rotate_cw<Type>(dest + size * 2,dest + size * 3,width,height,components);
	rotate_copy<Type>(dest + size * 3,src + size * 1,width,height,components);
	rotate_ccw<Type>(dest + size * 4,src + size * 2,width,height,components);
	rotate_ccw<Type>(dest + size * 5,src + size * 3,width,height,components);
	rotate_ccw<Type>(dest + size * 0,src + size * 4,width,height,components);
	rotate_cw<Type>(dest + size * 1,src + size * 5,width,height,components);
}

/*
 */
int VImage::rotate(int angle) {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::rotate(): image is not loaded\n");
		return 0;
	}
	
	// check m_PixelFormat
	if(isCombinedFormat() || isCompressedFormat()) {
		VLOG_ERROR("VImage::rotate(): can't rotate %s image\n",GetFormatName());
		return 0;
	}
	
	// check angle
	if(angle == 0) {
		return 1;
	}
	
	int components = GetPixelSize();
	if(isUCharFormat()) components /= 1;
	else if(isUShortFormat()) components /= 2;
	else if(isHalfFormat()) components /= 2;
	else if(isFloatFormat()) components /= 4;
	
	// 2d images
	if(m_Usage == VIU_2D) {
		
		for(int i = 0; i < abs(angle % 4); i++) {
			
			BYTE *src = m_Pixels;
			
			m_NumMipmaps = 1;
			m_Pixels = new BYTE[GetSize()];
			
			int old_width = width;
			width = height;
			height = old_width;
			
			if(angle > 0) {
				if(isUCharFormat()) rotate_cw<BYTE>(m_Pixels,src,width,height,components);
				else if(isUShortFormat()) rotate_cw<unsigned short>(m_Pixels,src,width,height,components);
				else if(isHalfFormat()) rotate_cw<half>(m_Pixels,src,width,height,components);
				else if(isFloatFormat()) rotate_cw<float>(m_Pixels,src,width,height,components);
				else VASSERT(0 && "VImage::rotate(): unknown image m_PixelFormat");
			} else {
				if(isUCharFormat()) rotate_ccw<BYTE>(m_Pixels,src,width,height,components);
				else if(isUShortFormat()) rotate_ccw<unsigned short>(m_Pixels,src,width,height,components);
				else if(isHalfFormat()) rotate_ccw<half>(m_Pixels,src,width,height,components);
				else if(isFloatFormat()) rotate_ccw<float>(m_Pixels,src,width,height,components);
				else VASSERT(0 && "VImage::rotate(): unknown image m_PixelFormat");
			}
			
			delete [] src;
		}
	}
	
	// cube images
	else if(m_Usage == VIU_CUBEMAP) {
		
		for(int i = 0; i < abs(angle % 3); i++) {
			
			BYTE *src = m_Pixels;
			
			m_NumMipmaps = 1;
			size_t size = GetSize();
			m_Pixels = new BYTE[size];
			size /= 6;
			
			if(angle > 0) {
				if(isUCharFormat()) rotate_cube_cw<BYTE>(m_Pixels,src,width,height,size,components);
				else if(isUShortFormat()) rotate_cube_cw<unsigned short>(m_Pixels,src,width,height,size,components);
				else if(isHalfFormat()) rotate_cube_cw<half>(m_Pixels,src,width,height,size,components);
				else if(isFloatFormat()) rotate_cube_cw<float>(m_Pixels,src,width,height,size,components);
				else VASSERT(0 && "VImage::rotate(): unknown image m_PixelFormat");
			} else {
				if(isUCharFormat()) rotate_cube_ccw<BYTE>(m_Pixels,src,width,height,size,components);
				else if(isUShortFormat()) rotate_cube_ccw<unsigned short>(m_Pixels,src,width,height,size,components);
				else if(isHalfFormat()) rotate_cube_ccw<half>(m_Pixels,src,width,height,size,components);
				else if(isFloatFormat()) rotate_cube_ccw<float>(m_Pixels,src,width,height,size,components);
				else VASSERT(0 && "VImage::rotate(): unknown image m_PixelFormat");
			}
			
			delete [] src;
		}
	}
	
	else {
		VLOG_ERROR("VImage::rotate(): can't rotate %s image\n",GetTypeName());
		return 0;
	}
	
	return 1;
}

/******************************************************************************\
*
* Blur image
*
\******************************************************************************/

/*
 */
template <class Type,class DType>
static void blur_2d_v(BYTE *dest,const BYTE *src,int width,int height,int pixels,int components) {
	Type *d = (Type*)dest;
	const Type *s = (Type*)src;
	int weight = pixels * 2 + 1;
	for(int y = 0; y < height; y++) {
		for(int x = 0; x < width; x++) {
			DType color[4] = { 0, 0, 0, 0 };
			for(int i = -pixels; i <= pixels; i++) {
				for(int j = 0; j < components; j++) {
					if(y + i < 0) color[j] += s[x * components + j];
					else if(y + i >= height) color[j] += s[((height - 1) * width + x) * components + j];
					else color[j] += s[((y + i) * width + x) * components + j];
				}
			}
			for(int i = 0; i < components; i++) {
				*d++ = (Type)(color[i] / weight);
			}
		}
	}
}

template <class Type,class DType>
static void blur_2d_h(BYTE *dest,const BYTE *src,int width,int height,int pixels,int components) {
	Type *d = (Type*)dest;
	const Type *s = (Type*)src;
	int weight = pixels * 2 + 1;
	for(int y = 0; y < height; y++) {
		for(int x = 0; x < width; x++) {
			DType color[4] = { 0, 0, 0, 0 };
			for(int i = -pixels; i <= pixels; i++) {
				for(int j = 0; j < components; j++) {
					if(x + i < 0) color[j] += s[width * y * components + j];
					else if(x + i >= width) color[j] += s[(width * y + width - 1) * components + j];
					else color[j] += s[(width * y + x + i) * components + j];
				}
			}
			for(int i = 0; i < components; i++) {
				*d++ = (Type)(color[i] / weight);
			}
		}
	}
}

/*
 */
int VImage::blur(int pixels) {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::blur(): image is not loaded\n");
		return 0;
	}
	
	// check m_PixelFormat
	if(isCombinedFormat() || isCompressedFormat()) {
		VLOG_ERROR("VImage::blur(): can't blur %s image\n",GetFormatName());
		return 0;
	}
	
	// check filter
	if(pixels == 0) {
		return 1;
	}
	
	// update m_PixelFormat
	int old_format = m_PixelFormat;
	if(isHalfFormat()) convertToFormat(m_PixelFormat + 4);
	
	int components = GetPixelSize();
	if(isUCharFormat()) components /= 1;
	else if(isUShortFormat()) components /= 2;
	else if(isFloatFormat()) components /= 4;
	
	// 2d images
	if(m_Usage == VIU_2D) {
		
		m_NumMipmaps = 1;
		BYTE *dest = new BYTE[GetSize()];
		
		if(isUCharFormat()) blur_2d_v<BYTE,int>(dest,m_Pixels,width,height,pixels,components);
		else if(isUShortFormat()) blur_2d_v<unsigned short,int>(dest,m_Pixels,width,height,pixels,components);
		else if(isFloatFormat()) blur_2d_v<float,float>(dest,m_Pixels,width,height,pixels,components);
		else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
		
		if(isUCharFormat()) blur_2d_h<BYTE,int>(m_Pixels,dest,width,height,pixels,components);
		else if(isUShortFormat()) blur_2d_h<unsigned short,int>(m_Pixels,dest,width,height,pixels,components);
		else if(isFloatFormat()) blur_2d_h<float,float>(m_Pixels,dest,width,height,pixels,components);
		else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
		
		delete [] dest;
	}
	
	// cube images
	else if(m_Usage == VIU_CUBEMAP) {
		
		m_NumMipmaps = 1;
		size_t size = GetSize();
		BYTE *dest = new BYTE[size];
		
		size /= 6;
		size_t stride = size / height;
		BYTE *buffer_0 = new BYTE[size * 9];
		BYTE *buffer_1 = new BYTE[size * 9];
		BYTE *buffer_2 = new BYTE[size * 2];
		
		const int angles[6][9] = {
			{ 0, 1, 2, 0, 0, 0, 0,-1,-2 },
			{-2,-1, 0, 0, 0, 0, 2, 1, 0 },
			{ 1, 2,-1, 1, 0,-1, 1, 0,-1 },
			{-1, 0, 1,-1, 0, 1,-1, 2, 1 },
			{-1, 0, 1, 0, 0, 0, 1, 0,-1 },
			{ 1, 2,-1, 0, 0, 0,-1,-2, 1 },
		};
		const int indices[6][9] = {
			{ 2, 2, 2, 4, 0, 5, 3, 3, 3 },
			{ 2, 2, 2, 5, 1, 4, 3, 3, 3 },
			{ 5, 5, 5, 1, 2, 0, 4, 4, 4 },
			{ 4, 4, 4, 1, 3, 0, 5, 5, 5 },
			{ 2, 2, 2, 1, 4, 0, 3, 3, 3 },
			{ 2, 2, 2, 0, 5, 1, 3, 3, 3 },
		};
		
		for(int i = 0; i < 6; i++) {
			
			for(int Y = 0; Y < 3; Y++) {
				for(int X = 0; X < 3; X++) {
					int id = Y * 3 + X;
					const BYTE *s = m_Pixels + size * indices[i][id];
					BYTE *d = buffer_0 + size * 3 * Y + stride * X;
					if(angles[i][id] > 0) {
						if(isUCharFormat()) rotate_cw<BYTE>(buffer_2,s,width,height,components);
						else if(isUShortFormat()) rotate_cw<unsigned short>(buffer_2,s,width,height,components);
						else if(isFloatFormat()) rotate_cw<float>(buffer_2,s,width,height,components);
						else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
						s = buffer_2;
						if(angles[i][id] > 1) {
							if(isUCharFormat()) rotate_cw<BYTE>(buffer_2 + size,s,width,height,components);
							else if(isUShortFormat()) rotate_cw<unsigned short>(buffer_2 + size,s,width,height,components);
							else if(isFloatFormat()) rotate_cw<float>(buffer_2 + size,s,width,height,components);
							else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
							s = buffer_2 + size;
						}
					} else if(angles[i][id] < 0) {
						if(isUCharFormat()) rotate_ccw<BYTE>(buffer_2,s,width,height,components);
						else if(isUShortFormat()) rotate_ccw<unsigned short>(buffer_2,s,width,height,components);
						else if(isFloatFormat()) rotate_ccw<float>(buffer_2,s,width,height,components);
						else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
						s = buffer_2;
						if(angles[i][id] < -1) {
							if(isUCharFormat()) rotate_ccw<BYTE>(buffer_2 + size,s,width,height,components);
							else if(isUShortFormat()) rotate_ccw<unsigned short>(buffer_2 + size,s,width,height,components);
							else if(isFloatFormat()) rotate_ccw<float>(buffer_2 + size,s,width,height,components);
							else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
							s = buffer_2 + size;
						}
					}
					for(int y = 0; y < height; y++) {
						memcpy(d,s,stride);
						d += stride * 3;
						s += stride;
					}
				}
			}
			
			if(isUCharFormat()) blur_2d_v<BYTE,int>(buffer_1,buffer_0,width * 3,height * 3,pixels,components);
			else if(isUShortFormat()) blur_2d_v<unsigned short,int>(buffer_1,buffer_0,width * 3,height * 3,pixels,components);
			else if(isFloatFormat()) blur_2d_v<float,float>(buffer_1,buffer_0,width * 3,height * 3,pixels,components);
			else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
			
			if(isUCharFormat()) blur_2d_h<BYTE,int>(buffer_0 + size * 3,buffer_1 + size * 3,width * 3,height,pixels,components);
			else if(isUShortFormat()) blur_2d_h<unsigned short,int>(buffer_0 + size * 3,buffer_1 + size * 3,width * 3,height,pixels,components);
			else if(isFloatFormat()) blur_2d_h<float,float>(buffer_0 + size * 3,buffer_1 + size * 3,width * 3,height,pixels,components);
			else VASSERT(0 && "VImage::blur(): unknown image m_PixelFormat");
			
			const BYTE *s = buffer_0 + size * 3 + stride;
			BYTE *d = dest + size * i;
			
			for(int y = 0; y < height; y++) {
				memcpy(d,s,stride);
				s += stride * 3;
				d += stride;
			}
		}
		
		delete [] buffer_0;
		delete [] buffer_1;
		delete [] buffer_2;
		
		delete [] m_Pixels;
		m_Pixels = dest;
	}
	
	else {
		VLOG_ERROR("VImage::blur(): can't blur %s image\n",GetTypeName());
		return 0;
	}
	
	// restore m_PixelFormat
	if(old_format != m_PixelFormat) convertToFormat(old_format);
	
	return 1;
}
/******************************************************************************\
*
* Flip image
*
\******************************************************************************/

/*
 */
template <class Type>
static void flip_x(BYTE *dest,int width,int height,int components) {
	for(int y = 0; y < height; y++) {
		Type *s = &((Type*)dest)[width * y * components];
		Type *d = &((Type*)dest)[(width * y + width - 1) * components];
		for(int x = 0; x < width / 2; x++) {
			for(int i = 0; i < components; i++) {
				Type c = *d;
				*d++ = *s;
				*s++ = c;
			}
			d -= components * 2;
		}
	}
}

/*
 */
int VImage::flipX() {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::flipX(): image is not loaded\n");
		return 0;
	}
	
	// check m_Usage
	if(m_Usage != VIU_2D) {
		VLOG_ERROR("VImage::flipX(): can't flip %s image\n",GetTypeName());
		return 0;
	}
	
	// check m_PixelFormat
	if(isCombinedFormat() || isCompressedFormat()) {
		VLOG_ERROR("VImage::flipX(): can't flip %s image\n",GetFormatName());
		return 0;
	}
	
	int components = GetPixelSize();
	if(isUCharFormat()) components /= 1;
	else if(isUShortFormat()) components /= 2;
	else if(isHalfFormat()) components /= 2;
	else if(isFloatFormat()) components /= 4;
	
	m_NumMipmaps = 1;
	
	if(isUCharFormat()) ::flip_x<BYTE>(m_Pixels,width,height,components);
	else if(isUShortFormat()) ::flip_x<unsigned short>(m_Pixels,width,height,components);
	else if(isHalfFormat()) ::flip_x<half>(m_Pixels,width,height,components);
	else if(isFloatFormat()) ::flip_x<float>(m_Pixels,width,height,components);
	else VASSERT(0 && "VImage::flipX(): unknown image m_PixelFormat");
	
	return 1;
}

/*
 */
template <class Type>
static void flip_y(BYTE *dest,int width,int height,int components) {
	for(int y = 0; y < height / 2; y++) {
		Type *s = &((Type*)dest)[width * y * components];
		Type *d = &((Type*)dest)[width * (height - y - 1) * components];
		for(int x = 0; x < width * components; x++) {
			Type c = *d;
			*d++ = *s;
			*s++ = c;
		}
	}
}

/*
 */
int VImage::flipY() {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::flipY(): image is not loaded\n");
		return 0;
	}
	
	// check m_Usage
	if(m_Usage != VIU_2D) {
		VLOG_ERROR("VImage::flipY(): can't flip %s image\n",GetTypeName());
		return 0;
	}
	
	// check m_PixelFormat
	if(isCombinedFormat() || isCompressedFormat()) {
		VLOG_ERROR("VImage::flipY(): can't flip %s image\n",GetFormatName());
		return 0;
	}
	
	int components = GetPixelSize();
	if(isUCharFormat()) components /= 1;
	else if(isUShortFormat()) components /= 2;
	else if(isHalfFormat()) components /= 2;
	else if(isFloatFormat()) components /= 4;
	
	m_NumMipmaps = 1;
	
	if(isUCharFormat()) ::flip_y<BYTE>(m_Pixels,width,height,components);
	else if(isUShortFormat()) ::flip_y<unsigned short>(m_Pixels,width,height,components);
	else if(isHalfFormat()) ::flip_y<half>(m_Pixels,width,height,components);
	else if(isFloatFormat()) ::flip_y<float>(m_Pixels,width,height,components);
	else VASSERT(0 && "VImage::flipY(): unknown image m_PixelFormat");
	
	return 1;
}


/******************************************************************************\
*
* Convert to m_PixelFormat
*
\******************************************************************************/

/*
 */
static inline int clamp_color_5(int color) {
	if(color > 31) return 31;
	if(color < 0) return 0;
	return color;
}

static inline int clamp_color_8(int color) {
	if(color > 255) return 255;
	if(color < 0) return 0;
	return color;
}

static inline int clamp_color_10(int color) {
	if(color > 1023) return 1023;
	if(color < 0) return 0;
	return color;
}

/*
 */
static inline int compress_color_4(int color) {
	return (int)(color * (15.0f / 255.0f) + 0.5f);
}

static inline int compress_color_5(int color) {
	return (int)(color * (31.0f / 255.0f) + 0.5f);
}

static inline int compress_color_6(int color) {
	return (int)(color * (63.0f / 255.0f) + 0.5f);
}

static inline int compress_color_10(int color) {
	return (int)(color * (1023.0f / 65535.0f) + 0.5f);
}

/*
 */
static inline int decompress_color_4(int color) {
	return color * 255 / 15;
}

static inline int decompress_color_5(int color) {
	return color * 255 / 31;
}

static inline int decompress_color_6(int color) {
	return color * 255 / 63;
}

/*
 */
template <class DType,int DOne,class SType,int SOne>
static void r_to_r(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d += 1;
			s += 1;
		}
	} else {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)s[0];
			d += 1;
			s += 1;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void r_to_rg(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = 0;
			d += 2;
			s += 1;
		}
	} else {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)s[0];
			d[1] = 0;
			d += 2;
			s += 1;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void r_to_rgb(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = 0;
			d[2] = 0;
			d += 3;
			s += 1;
		}
	} else {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)s[0];
			d[1] = 0;
			d[2] = 0;
			d += 3;
			s += 1;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void r_to_rgba(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = 0;
			d[2] = 0;
			d[3] = DOne;
			d += 4;
			s += 1;
		}
	} else {
		for(size_t i = 0; i < size; i += 1) {
			d[0] = (DType)s[0];
			d[1] = 0;
			d[2] = 0;
			d[3] = DOne;
			d += 4;
			s += 1;
		}
	}
}

/*
 */
template <class DType,int DOne,class SType,int SOne>
static void rg_to_r(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d += 1;
			s += 2;
		}
	} else {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)s[0];
			d += 1;
			s += 2;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rg_to_rg(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d += 2;
			s += 2;
		}
	} else {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d += 2;
			s += 2;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rg_to_rgb(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d[2] = 0;
			d += 3;
			s += 2;
		}
	} else {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d[2] = 0;
			d += 3;
			s += 2;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rg_to_rgba(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d[2] = 0;
			d[3] = DOne;
			d += 4;
			s += 2;
		}
	} else {
		for(size_t i = 0; i < size; i += 2) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d[2] = 0;
			d[3] = DOne;
			d += 4;
			s += 2;
		}
	}
}

/*
 */
template <class DType,int DOne,class SType,int SOne>
static void rgb_to_r(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d += 1;
			s += 3;
		}
	} else {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)s[0];
			d += 1;
			s += 3;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rgb_to_rg(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d += 2;
			s += 3;
		}
	} else {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d += 2;
			s += 3;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rgb_to_rgb(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d[2] = (DType)(s[2] * DOne / SOne);
			d += 3;
			s += 3;
		}
	} else {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d[2] = (DType)s[2];
			d += 3;
			s += 3;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rgb_to_rgba(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d[2] = (DType)(s[2] * DOne / SOne);
			d[3] = DOne;
			d += 4;
			s += 3;
		}
	} else {
		for(size_t i = 0; i < size; i += 3) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d[2] = (DType)s[2];
			d[3] = DOne;
			d += 4;
			s += 3;
		}
	}
}

/*
 */
template <class DType,int DOne,class SType,int SOne>
static void rgba_to_r(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d += 1;
			s += 4;
		}
	} else {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)s[0];
			d += 1;
			s += 4;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rgba_to_rg(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d += 2;
			s += 4;
		}
	} else {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d += 2;
			s += 4;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rgba_to_rgb(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d[2] = (DType)(s[2] * DOne / SOne);
			d += 3;
			s += 4;
		}
	} else {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d[2] = (DType)s[2];
			d += 3;
			s += 4;
		}
	}
}

template <class DType,int DOne,class SType,int SOne>
static void rgba_to_rgba(BYTE *dest,BYTE *src,size_t size) {
	DType *d = (DType*)dest;
	SType *s = (SType*)src;
	if(sizeof(DType) != sizeof(SType)) {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)(s[0] * DOne / SOne);
			d[1] = (DType)(s[1] * DOne / SOne);
			d[2] = (DType)(s[2] * DOne / SOne);
			d[3] = (DType)(s[3] * DOne / SOne);
			d += 4;
			s += 4;
		}
	} else {
		for(size_t i = 0; i < size; i += 4) {
			d[0] = (DType)s[0];
			d[1] = (DType)s[1];
			d[2] = (DType)s[2];
			d[3] = (DType)s[3];
			d += 4;
			s += 4;
		}
	}
}

/*
 */
static void ati1_to_dxt1(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 8) {
		ImageFormat::convertDXT5AlphaToDXT1ColorBlock(m_Pixels);
		m_Pixels += 8;
	}
}

static void ati2_to_dxt5(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 16) {
		ImageFormat::convertDXT5AlphaToDXT1ColorBlock(m_Pixels + 8);
		m_Pixels += 16;
	}
}

/*
 */
static void dxt1_to_ati1(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 8) {
		ImageFormat::convertDXT1ColorToDXT5AlphaBlock(m_Pixels);
		m_Pixels += 8;
	}
}

static void dxt5_to_ati2(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 16) {
		ImageFormat::convertDXT1ColorToDXT5AlphaBlock(m_Pixels + 8);
		m_Pixels += 16;
	}
}

/*
 */
static void ati1_to_atc1(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 8) {
		ImageFormat::convertDXT5AlphaToATC1ColorBlock(m_Pixels);
		m_Pixels += 8;
	}
}

static void ati2_to_atc5(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 16) {
		ImageFormat::convertDXT5AlphaToATC1ColorBlock(m_Pixels + 8);
		m_Pixels += 16;
	}
}

/*
 */
static void atc1_to_ati1(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 8) {
		ImageFormat::convertATC1ColorToDXT5AlphaBlock(m_Pixels);
		m_Pixels += 8;
	}
}

static void atc5_to_ati2(BYTE *m_Pixels,size_t size) {
	for(size_t i = 0; i < size; i += 16) {
		ImageFormat::convertATC1ColorToDXT5AlphaBlock(m_Pixels + 8);
		m_Pixels += 16;
	}
}

/*
 */
int VImage::convertToFormat(int new_format) {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::convertToFormat(): image is not loaded\n");
		return 0;
	}
	
	// same m_PixelFormat
	if(m_PixelFormat == new_format) {
		
	}
	
	// combined m_PixelFormat
	else if(isCombinedFormat()) {
		if(decombine() == 0) return 0;
		return convertToFormat(new_format);
	}
	else if(new_format == FORMAT_R5G6B5) {
		if(convertToFormat(FORMAT_RGB8) == 0) return 0;
		return combine();
	}
	else if(new_format == FORMAT_RGBA4) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return combine(FORMAT_RGBA4);
	}
	else if(new_format == FORMAT_RGB5A1) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return combine(FORMAT_RGB5A1);
	}
	else if(new_format == FORMAT_RGB10A2) {
		if(convertToFormat(FORMAT_RGBA16) == 0) return 0;
		return combine(FORMAT_RGB10A2);
	}
	
	// compressed m_PixelFormat
	else if(isCompressedFormat()) {
		if(m_PixelFormat == FORMAT_ATI1 && new_format == FORMAT_DXT1) {
			ati1_to_dxt1(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		} else if(m_PixelFormat == FORMAT_ATI2 && new_format == FORMAT_DXT5) {
			ati2_to_dxt5(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		} else if(m_PixelFormat == FORMAT_DXT1 && new_format == FORMAT_ATI1) {
			dxt1_to_ati1(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		} else if(m_PixelFormat == FORMAT_DXT5 && new_format == FORMAT_ATI2) {
			dxt5_to_ati2(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		}
		else if(m_PixelFormat == FORMAT_ATI1 && new_format == FORMAT_ATC1) {
			ati1_to_atc1(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		} else if(m_PixelFormat == FORMAT_ATI2 && new_format == FORMAT_ATC5) {
			ati2_to_atc5(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		} else if(m_PixelFormat == FORMAT_ATC1 && new_format == FORMAT_ATI1) {
			atc1_to_ati1(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		} else if(m_PixelFormat == FORMAT_ATC5 && new_format == FORMAT_ATI2) {
			atc5_to_ati2(m_Pixels,GetSize());
			m_PixelFormat = new_format;
		}
		else if(new_format == FORMAT_ZLC1) {
			if(isZCompressedFormat() && decompress() == 0) return 0;
			return compress(FORMAT_ZLC1);
		}
		else if(new_format == FORMAT_ZLC2) {
			if(isZCompressedFormat() && decompress() == 0) return 0;
			return compress(FORMAT_ZLC2);
		}
		else {
			if(decompress() == 0) return 0;
			return convertToFormat(new_format);
		}
	}
	else if(new_format == FORMAT_DXT1) {
		if(convertToFormat(FORMAT_RGB8) == 0) return 0;
		return compress(FORMAT_DXT1);
	}
	else if(new_format == FORMAT_DXT3) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return compress(FORMAT_DXT3);
	}
	else if(new_format == FORMAT_DXT5) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return compress(FORMAT_DXT5);
	}
	else if(new_format == FORMAT_ATI1) {
		if(convertToFormat(FORMAT_R8) == 0) return 0;
		return compress(FORMAT_ATI1);
	}
	else if(new_format == FORMAT_ATI2) {
		if(convertToFormat(FORMAT_RG8) == 0) return 0;
		return compress(FORMAT_ATI2);
	}
	else if(new_format == FORMAT_ETC1) {
		if(convertToFormat(FORMAT_RGB8) == 0) return 0;
		return compress(FORMAT_ETC1);
	}
	else if(new_format == FORMAT_ETC2) {
		if(convertToFormat(FORMAT_RGB8) == 0) return 0;
		return compress(FORMAT_ETC2);
	}
	else if(new_format == FORMAT_ETC5) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return compress(FORMAT_ETC5);
	}
	else if(new_format == FORMAT_EAC1) {
		if(convertToFormat(FORMAT_R8) == 0) return 0;
		return compress(FORMAT_EAC1);
	}
	else if(new_format == FORMAT_EAC2) {
		if(convertToFormat(FORMAT_RG8) == 0) return 0;
		return compress(FORMAT_EAC2);
	}
	else if(new_format == FORMAT_ATC1) {
		if(convertToFormat(FORMAT_RGB8) == 0) return 0;
		return compress(FORMAT_ATC1);
	}
	else if(new_format == FORMAT_ATC3) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return compress(FORMAT_ATC3);
	}
	else if(new_format == FORMAT_ATC5) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return compress(FORMAT_ATC5);
	}
	else if(new_format == FORMAT_PVR4) {
		if(convertToFormat(FORMAT_RGBA8) == 0) return 0;
		return compress(FORMAT_PVR4);
	}
	else if(new_format == FORMAT_ZLC1) {
		return compress(FORMAT_ZLC1);
	}
	else if(new_format == FORMAT_ZLC2) {
		return compress(FORMAT_ZLC2);
	}
	
	// raw formats
	else if(isRawFormat()) {
		
		size_t size = GetSize();
		BYTE *src = m_Pixels;
		
		// 8-bits per pixel unsigned formats
		if(m_PixelFormat == FORMAT_R8) {
			if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[size * 2];
				r_to_rg<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[size * 3];
				r_to_rgb<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[size * 4];
				r_to_rgba<BYTE,255,BYTE,255>(m_Pixels,src,size);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[size * 2];
				r_to_r<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[size * 4];
				r_to_rg<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[size * 6];
				r_to_rgb<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[size * 8];
				r_to_rgba<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RG8) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 2) * 1];
				rg_to_r<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[(size / 2) * 3];
				rg_to_rgb<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[(size / 2) * 4];
				rg_to_rgba<BYTE,255,BYTE,255>(m_Pixels,src,size);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[(size / 2) * 2];
				rg_to_r<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[(size / 2) * 4];
				rg_to_rg<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[(size / 2) * 6];
				rg_to_rgb<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[(size / 2) * 8];
				rg_to_rgba<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGB8) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 3) * 1];
				rgb_to_r<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[(size / 3) * 2];
				rgb_to_rg<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[(size / 3) * 4];
				rgb_to_rgba<BYTE,255,BYTE,255>(m_Pixels,src,size);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[(size / 3) * 2];
				rgb_to_r<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[(size / 3) * 4];
				rgb_to_rg<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[(size / 3) * 6];
				rgb_to_rgb<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[(size / 3) * 8];
				rgb_to_rgba<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGBA8) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 4) * 1];
				rgba_to_r<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rgba_to_rg<BYTE,255,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[(size / 4) * 3];
				rgba_to_rgb<BYTE,255,BYTE,255>(m_Pixels,src,size);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rgba_to_r<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[(size / 4) * 4];
				rgba_to_rg<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[(size / 4) * 6];
				rgba_to_rgb<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[(size / 4) * 8];
				rgba_to_rgba<unsigned short,65535,BYTE,255>(m_Pixels,src,size);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		
		// 16-bits per pixel unsigned formats
		else if(m_PixelFormat == FORMAT_R16) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 2) * 1];
				r_to_r<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[(size / 2) * 2];
				r_to_rg<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[(size / 2) * 3];
				r_to_rgb<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[(size / 2) * 4];
				r_to_rgba<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[size * 2];
				r_to_rg<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[size * 3];
				r_to_rgb<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[size * 4];
				r_to_rgba<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RG16) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 4) * 1];
				rg_to_r<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rg_to_rg<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[(size / 4) * 3];
				rg_to_rgb<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[(size / 4) * 4];
				rg_to_rgba<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[(size / 2) * 1];
				rg_to_r<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[(size / 2) * 3];
				rg_to_rgb<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[(size / 2) * 4];
				rg_to_rgba<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGB16) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 6) * 1];
				rgb_to_r<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[(size / 6) * 2];
				rgb_to_rg<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[(size / 6) * 3];
				rgb_to_rgb<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[(size / 6) * 4];
				rgb_to_rgba<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[(size / 3) * 1];
				rgb_to_r<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[(size / 3) * 2];
				rgb_to_rg<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA16) {
				m_Pixels = new BYTE[(size / 3) * 4];
				rgb_to_rgba<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGBA16) {
			if(new_format == FORMAT_R8) {
				m_Pixels = new BYTE[(size / 8) * 1];
				rgba_to_r<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG8) {
				m_Pixels = new BYTE[(size / 8) * 2];
				rgba_to_rg<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB8) {
				m_Pixels = new BYTE[(size / 8) * 3];
				rgba_to_rgb<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA8) {
				m_Pixels = new BYTE[(size / 8) * 4];
				rgba_to_rgba<BYTE,255,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R16) {
				m_Pixels = new BYTE[(size / 4) * 1];
				rgba_to_r<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG16) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rgba_to_rg<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB16) {
				m_Pixels = new BYTE[(size / 4) * 3];
				rgba_to_rgb<unsigned short,65535,unsigned short,65535>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		
		// 16-bits per pixel float formats
		else if(m_PixelFormat == FORMAT_R16F) {
			if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[size * 2];
				r_to_rg<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[size * 3];
				r_to_rgb<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[size * 4];
				r_to_rgba<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[size * 2];
				r_to_r<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[size * 4];
				r_to_rg<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[size * 6];
				r_to_rgb<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[size * 8];
				r_to_rgba<float,1,half,1>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RG16F) {
			if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 2) * 1];
				rg_to_r<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[(size / 2) * 3];
				rg_to_rgb<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[(size / 2) * 4];
				rg_to_rgba<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[(size / 2) * 2];
				rg_to_r<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[(size / 2) * 4];
				rg_to_rg<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[(size / 2) * 6];
				rg_to_rgb<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[(size / 2) * 8];
				rg_to_rgba<float,1,half,1>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGB16F) {
			if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 3) * 1];
				rgb_to_r<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[(size / 3) * 2];
				rgb_to_rg<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[(size / 3) * 4];
				rgb_to_rgba<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[(size / 3) * 2];
				rgb_to_r<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[(size / 3) * 4];
				rgb_to_rg<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[(size / 3) * 6];
				rgb_to_rgb<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[(size / 3) * 8];
				rgb_to_rgba<float,1,half,1>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGBA16F) {
			if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 4) * 1];
				rgba_to_r<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rgba_to_rg<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[(size / 4) * 3];
				rgba_to_rgb<unsigned short,0x3c00,unsigned short,0x3c00>(m_Pixels,src,size / 2);
			}
			else if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rgba_to_r<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[(size / 4) * 4];
				rgba_to_rg<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[(size / 4) * 6];
				rgba_to_rgb<float,1,half,1>(m_Pixels,src,size / 2);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[(size / 4) * 8];
				rgba_to_rgba<float,1,half,1>(m_Pixels,src,size / 2);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		
		// 32-bits per pixel float formats
		else if(m_PixelFormat == FORMAT_R32F) {
			if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[size * 2];
				r_to_rg<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[size * 3];
				r_to_rgb<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[size * 4];
				r_to_rgba<float,1,float,1>(m_Pixels,src,size / 4);
			}
			else if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 4) * 2];
				r_to_r<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[(size / 4) * 4];
				r_to_rg<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[(size / 4) * 6];
				r_to_rgb<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[(size / 4) * 8];
				r_to_rgba<half,1,float,1>(m_Pixels,src,size / 4);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RG32F) {
			if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[(size / 2) * 1];
				rg_to_r<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[(size / 2) * 3];
				rg_to_rgb<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[(size / 2) * 4];
				rg_to_rgba<float,1,float,1>(m_Pixels,src,size / 4);
			}
			else if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 8) * 2];
				rg_to_r<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[(size / 8) * 4];
				rg_to_rg<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[(size / 8) * 6];
				rg_to_rgb<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[(size / 8) * 8];
				rg_to_rgba<half,1,float,1>(m_Pixels,src,size / 4);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGB32F) {
			if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[(size / 3) * 1];
				rgb_to_r<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[(size / 3) * 2];
				rgb_to_rg<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA32F) {
				m_Pixels = new BYTE[(size / 3) * 4];
				rgb_to_rgba<float,1,float,1>(m_Pixels,src,size / 4);
			}
			else if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 12) * 2];
				rgb_to_r<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[(size / 12) * 4];
				rgb_to_rg<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[(size / 12) * 6];
				rgb_to_rgb<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[(size / 12) * 8];
				rgb_to_rgba<half,1,float,1>(m_Pixels,src,size / 4);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		else if(m_PixelFormat == FORMAT_RGBA32F) {
			if(new_format == FORMAT_R32F) {
				m_Pixels = new BYTE[(size / 4) * 1];
				rgba_to_r<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RG32F) {
				m_Pixels = new BYTE[(size / 4) * 2];
				rgba_to_rg<float,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB32F) {
				m_Pixels = new BYTE[(size / 4) * 3];
				rgba_to_rgb<float,1,float,1>(m_Pixels,src,size / 4);
			}
			else if(new_format == FORMAT_R16F) {
				m_Pixels = new BYTE[(size / 16) * 2];
				rgba_to_r<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RG16F) {
				m_Pixels = new BYTE[(size / 16) * 4];
				rgba_to_rg<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGB16F) {
				m_Pixels = new BYTE[(size / 16) * 6];
				rgba_to_rgb<half,1,float,1>(m_Pixels,src,size / 4);
			} else if(new_format == FORMAT_RGBA16F) {
				m_Pixels = new BYTE[(size / 16) * 8];
				rgba_to_rgba<half,1,float,1>(m_Pixels,src,size / 4);
			}
			else {
				VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
				return 0;
			}
		}
		
		else {
			VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
			return 0;
		}
		
		m_PixelFormat = new_format;
		delete [] src;
	}
	
	else {
		VLOG_ERROR("VImage::convertToFormat(): can't convert %s to %s\n",GetFormatName(),format_names[new_format]);
		return 0;
	}
	
	return 1;
}

/******************************************************************************\
*
* Combine image
*
\******************************************************************************/

/*
 */
int VImage::decombine() {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::decombine(): image is not loaded\n");
		return 0;
	}
	
	size_t size = GetSize();
	BYTE *src = m_Pixels;
	
	if(m_PixelFormat == FORMAT_R5G6B5) {
		m_Pixels = new BYTE[(size / 2) * 3];
		BYTE *s = src;
		BYTE *d = m_Pixels;
		for(size_t i = 0; i < size; i += 2) {
			#ifdef USE_BIG_ENDIAN
				unsigned short color = s[0];
				color |= (unsigned short)s[1] << 8;
			#else
				unsigned short color = *(unsigned short*)s;
			#endif
			d[0] = decompress_color_5((color & 0xf800) >> 11);
			d[1] = decompress_color_6((color & 0x07e0) >>  5);
			d[2] = decompress_color_5((color & 0x001f) >>  0);
			s += 2;
			d += 3;
		}
		m_PixelFormat = FORMAT_RGB8;
	}
	else if(m_PixelFormat == FORMAT_RGBA4) {
		m_Pixels = new BYTE[(size / 2) * 4];
		BYTE *s = src;
		BYTE *d = m_Pixels;
		for(size_t i = 0; i < size; i += 2) {
			#ifdef USE_BIG_ENDIAN
				unsigned short color = s[0];
				color |= (unsigned short)s[1] << 8;
			#else
				unsigned short color = *(unsigned short*)s;
			#endif
			d[0] = decompress_color_4((color & 0x0f00) >>  8);
			d[1] = decompress_color_4((color & 0x00f0) >>  4);
			d[2] = decompress_color_4((color & 0x000f) >>  0);
			d[3] = decompress_color_4((color & 0xf000) >> 12);
			s += 2;
			d += 4;
		}
		m_PixelFormat = FORMAT_RGBA8;
	}
	else if(m_PixelFormat == FORMAT_RGB5A1) {
		m_Pixels = new BYTE[(size / 2) * 4];
		BYTE *s = src;
		BYTE *d = m_Pixels;
		for(size_t i = 0; i < size; i += 2) {
			#ifdef USE_BIG_ENDIAN
				unsigned short color = s[0];
				color |= (unsigned short)s[1] << 8;
			#else
				unsigned short color = *(unsigned short*)s;
			#endif
			d[0] = decompress_color_5((color & 0x7c00) >> 10);
			d[1] = decompress_color_5((color & 0x03e0) >>  5);
			d[2] = decompress_color_5((color & 0x001f) >>  0);
			d[3] = ((color & 0x8000) >> 15) * 255;
			s += 2;
			d += 4;
		}
		m_PixelFormat = FORMAT_RGBA8;
	}
	else if(m_PixelFormat == FORMAT_RGB10A2) {
		m_Pixels = new BYTE[(size / 4) * 8];
		BYTE *s = src;
		unsigned short *d = (unsigned short*)m_Pixels;
		for(size_t i = 0; i < size; i += 4) {
			#ifdef USE_BIG_ENDIAN
				unsigned int color = s[0];
				color |= (unsigned int)s[1] << 8;
				color |= (unsigned int)s[2] << 16;
				color |= (unsigned int)s[3] << 24;
			#else
				unsigned int color = *(unsigned int*)s;
			#endif
			d[0] = ((color & 0x000003ff) >>  0) * 65535 / 1023;
			d[1] = ((color & 0x000ffc00) >> 10) * 65535 / 1023;
			d[2] = ((color & 0x3ff00000) >> 20) * 65535 / 1023;
			d[3] = ((color & 0xc0000000) >> 30) * 65535 / 3;
			s += 4;
			d += 4;
		}
		m_PixelFormat = FORMAT_RGBA16;
	}
	else {
		VLOG_ERROR("VImage::decombine(): can't decombine %s image\n",GetFormatName());
		return 0;
	}
	
	delete [] src;
	
	return 1;
}

/*
 */
int VImage::combine(int new_format) {
	
	// check image
	if(isLoaded() == 0) {
		VLOG_ERROR("VImage::combine(): image is not loaded\n");
		return 0;
	}
	
	size_t size = GetSize();
	BYTE *src = m_Pixels;
	
	if(m_PixelFormat == FORMAT_RGB8) {
		m_Pixels = new BYTE[(size / 3) * 2];
		BYTE *s = src;
		BYTE *d = m_Pixels;
		for(size_t i = 0; i < size; i += 3) {
			unsigned short color = compress_color_5(s[0]) << 11;
			color |= compress_color_6(s[1]) << 5;
			color |= compress_color_5(s[2]) << 0;
			#ifdef USE_BIG_ENDIAN
				d[0] = (BYTE)((color >> 0) & 0xff);
				d[1] = (BYTE)((color >> 8) & 0xff);
			#else
				*(unsigned short*)d = color;
			#endif
			s += 3;
			d += 2;
		}
		m_PixelFormat = FORMAT_R5G6B5;
	}
	else if(m_PixelFormat == FORMAT_RGBA8) {
		m_Pixels = new BYTE[(size / 4) * 2];
		BYTE *s = src;
		BYTE *d = m_Pixels;
		if(new_format == FORMAT_RGBA4) {
			for(size_t i = 0; i < size; i += 4) {
				unsigned short color = compress_color_4(s[0]) << 8;
				color |= compress_color_4(s[1]) << 4;
				color |= compress_color_4(s[2]) << 0;
				color |= compress_color_4(s[3]) << 12;
				#ifdef USE_BIG_ENDIAN
					d[0] = (BYTE)((color >> 0) & 0xff);
					d[1] = (BYTE)((color >> 8) & 0xff);
				#else
					*(unsigned short*)d = color;
				#endif
				s += 4;
				d += 2;
			}
			m_PixelFormat = FORMAT_RGBA4;
		} else {
			for(size_t i = 0; i < size; i += 4) {
				unsigned short color = compress_color_5(s[0]) << 10;
				color |= compress_color_5(s[1]) << 5;
				color |= compress_color_5(s[2]) << 0;
				color |= (s[3]) ? 0x8000 : 0;
				#ifdef USE_BIG_ENDIAN
					d[0] = (BYTE)((color >> 0) & 0xff);
					d[1] = (BYTE)((color >> 8) & 0xff);
				#else
					*(unsigned short*)d = color;
				#endif
				s += 4;
				d += 2;
			}
			m_PixelFormat = FORMAT_RGB5A1;
		}
	}
	else if(m_PixelFormat == FORMAT_RGBA16) {
		m_Pixels = new BYTE[(size / 8) * 4];
		unsigned short *s = (unsigned short*)src;
		BYTE *d = m_Pixels;
		for(size_t i = 0; i < size; i += 8) {
			unsigned int color = compress_color_10(s[0]) << 0;
			color |= compress_color_10(s[1]) << 10;
			color |= compress_color_10(s[2]) << 20;
			color |= (s[3] >> 14) << 30;
			#ifdef USE_BIG_ENDIAN
				d[0] = (BYTE)((color >>  0) & 0xff);
				d[1] = (BYTE)((color >>  8) & 0xff);
				d[2] = (BYTE)((color >> 16) & 0xff);
				d[3] = (BYTE)((color >> 24) & 0xff);
			#else
				*(unsigned int*)d = color;
			#endif
			s += 4;
			d += 4;
		}
		m_PixelFormat = FORMAT_RGB10A2;
	}
	else {
		VLOG_ERROR("VImage::combine(): can't combine %s image\n",GetFormatName());
		return 0;
	}
	
	delete [] src;
	
	return 1;
}

#endif 